package com.example.web.aop;

import com.google.common.collect.Iterators;

import com.alibaba.fastjson.JSON;
import com.example.starter.exception.CheckedException;
import com.example.starter.exception.FieldErrorEntity;
import com.example.starter.exception.UnloginException;
import com.example.starter.dto.ResultBean;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;

/**
 * Created by liuw on 2017/11/14.
 */
@Aspect
@Component//@aspecj不会自动加载到spring
public class ControllerAOP {

    private static final Logger logger = LoggerFactory.getLogger(ControllerAOP.class);

    @Around("execution(public com.example.starter.dto.ResultBean *(..))")
    public Object handelerControllerMethod(ProceedingJoinPoint point) {
        long startTime = System.currentTimeMillis();
        ResultBean<?> result;

        try {
            result = (ResultBean<?>) point.proceed();
            logger.info(point.getSignature() + "use time:" + (System.currentTimeMillis() - startTime));
        } catch (Throwable e) {

            result = handlerException(point, e);
        }

        return result;
    }

    private ResultBean<?> handlerException(ProceedingJoinPoint pjp, Throwable e) {
        ResultBean<?> result = new ResultBean();

        // 已知异常
        if (e instanceof CheckedException) {
            result.setMsg(e.getLocalizedMessage());
            result.setCode(ResultBean.FAIL);
        } else if (e instanceof UnloginException) {
            result.setMsg("Unlogin");
            result.setCode(ResultBean.NO_LOGIN);
        } else if (e instanceof ConstraintViolationException) {
            final List<?> collect = ((ConstraintViolationException) e).getConstraintViolations().stream()
                                                                      .map(this::mapToFieldErrorEntity).collect(Collectors.toList());

            result.setCode(ResultBean.BEAN_VALIDATION_ERROR);
            //todo 待优化，msg结构特殊，解析复杂
            result.setMsg(JSON.toJSONString(collect));
        } else {
            logger.error(pjp.getSignature() + " error ", e);

            //TODO 未知的异常，应该格外注意，可以发送邮件通知等
            result.setMsg(e.toString());
            result.setCode(ResultBean.FAIL);
        }

        return result;
    }

    private FieldErrorEntity mapToFieldErrorEntity(ConstraintViolation<?> violation) {
        return new FieldErrorEntity(getFieldName(violation),
                                    toUtf8String(violation.getMessage()));
    }

    private String getFieldName(ConstraintViolation<?> violation) {
        Path path = violation.getPropertyPath();
        return Iterators.getLast(path.iterator()).getName();
    }

    private String toUtf8String(String origin) {
        try {
            return new String(origin.getBytes("ISO-8859-1"), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return origin;
    }
}
